/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.xeerkat.xmpp;

import java.io.IOException;
import java.io.Writer;
import java.util.Map;
import java.util.logging.Logger;
import org.infoset.xml.Document;
import org.infoset.xml.Element;
import org.infoset.xml.InfosetFactory;
import org.infoset.xml.Item;
import org.infoset.xml.ItemConstructor;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.ItemSource;
import org.infoset.xml.Name;
import org.infoset.xml.XMLException;
import org.infoset.xml.util.DocumentSource;
import org.infoset.xml.util.WriterItemDestination;

/**
 *
 * @author alex
 */
public class StanzaWriter {

   boolean active;
   Logger log;
   Writer output;
   WriterItemDestination outputXML;
   ItemConstructor constructor;
   ItemDestination stanzaDestination;
   
   public StanzaWriter(Logger log,Writer output) {
      this.active = false;
      this.log = log;
      this.output = output;
      this.outputXML = new WriterItemDestination(output,"UTF-8");
      this.constructor = InfosetFactory.getDefaultInfoset().createItemConstructor();
      this.stanzaDestination = new ItemDestination() {
         int level = 0;
         public void send(Item item)
            throws XMLException
         {
            switch (item.getType()) {
               case DocumentItem:
               case DocumentEndItem:
                  break;
               case ElementItem:
                  outputXML.send(item);
                  level++;
                  break;
               case ElementEndItem:
                  outputXML.send(item);
                  level--;
                  if (level==0) {
                     // TODO: this is because the serializer leaves the start tag open if it is empty till the next item
                     outputXML.send(constructor.createCharacters("\n"));
                     try {
                        StanzaWriter.this.output.flush();
                     } catch (IOException ex) {
                        throw new XMLException("Cannot flush stream for stanza write.",ex);
                     }
                     release();
                  }
                  break;
               default:
                  if (level!=0) {
                     outputXML.send(item);
                  }
            }  
         }
      };
   }
   
   public ItemConstructor getConstructor() {
      return constructor;
   }
   
   public boolean isActive() {
      return active;
   }

   public void start(Map<Name,String> properties) 
      throws XMLException
   {
      this.active = true;
      outputXML.send(constructor.createDocument());
      Element stream = constructor.createElement(Protocol.STREAM);
      stream.addNamespaceBinding(Name.NO_PREFIX,Protocol.XMPP_CLIENT);
      stream.addNamespaceBinding("server",Protocol.XMPP_SERVER);
      stream.addNamespaceBinding("db",Protocol.XMPP_DIALBACK);
      stream.addNamespaceBinding("stream",Protocol.XMPP_STREAM);
      if (properties!=null) {
         for (Name name : properties.keySet()) {
            stream.setAttributeValue(name, properties.get(name));
         }
      }
      stream.setAttributeValue("version","1.0");
      outputXML.send(stream);
      // TODO: this is because the serializer leaves the start tag open if it is empty till the next item
      outputXML.send(constructor.createCharacters("\n"));
      try {
         output.flush();
      } catch (IOException ex) {
         throw new XMLException("Cannot flush stream for start stream.",ex);
      }
   }
   
   public ItemDestination next() {
      return active ? stanzaDestination : null;
   }
   
   public void sendStanza(ItemSource source)
      throws XMLException
   {
      ItemDestination dest = next();
      Item item = null;
      while ((item=source.receive())!=null) {
         dest.send(item);
      }
   }
   
   public void sendStanza(Document source)
      throws XMLException
   {
      DocumentSource.generate(source.getDocumentElement(), false, next());
   }
   
   public void end() 
      throws XMLException
   {
      if (this.active) {
         outputXML.send(constructor.createElementEnd(Protocol.STREAM));
         outputXML.send(constructor.createDocumentEnd());
         this.active = false;
         try {
            output.flush();
         } catch (IOException ex) {
            throw new XMLException("Cannot flush stream for end stream.",ex);
         }
      }
   }
   
   public void terminate() {
      this.active = false;
   }
   
   public void release() {
   }
}
